
#include "ZipUtil.h"
#include "SysUtil.h"
#include "FileUtil.h"
#include "xUtils.h"

using namespace x2lib;
using namespace unzip;

#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz))

ZipUtil::ZipUtil(const char* pszZipFile, const char* pszPwd)
{
    strcpy(m_szZipFile, pszZipFile);
    strcpy(m_szPwd, pszPwd);
    sprintf(m_szZipFileTemp, "%s.ziptemp", m_szZipFile);
    m_hZip = OpenZip(m_szZipFile, m_szPwd);
    if (m_hZip == NULL) { m_hZip = CreateZip(m_szZipFile, m_szPwd); }
    m_pZipData = nullptr; 
    m_pZipDataTemp = nullptr;
    m_fileMode = true;
}

ZipUtil::ZipUtil(char* pZipData, int nZipData, const char* pszPwd)
{
    memset(m_szZipFile, 0, sizeof(m_szZipFile));
    memset(m_szPwd, 0, sizeof(m_szPwd));
    memset(m_szZipFileTemp, 0, sizeof(m_szZipFileTemp));
    m_pZipData = (char*)calloc(1, nZipData);
    memcpy(m_pZipData, pZipData, nZipData);
    m_hZip = OpenZip(m_pZipData, nZipData, m_szPwd);
    if (m_hZip == NULL) { m_hZip = CreateZip(m_pZipData, nZipData, m_szPwd); }
    m_pZipDataTemp = nullptr;
    m_fileMode = false;
}

ZipUtil::~ZipUtil()
{
    if (m_pZipData) { free(m_pZipData); }
    if (m_hZip) { CloseZip(m_hZip); }
}

uint32_t ZipUtil::GetUnCompressSize(const char* pszInFile, const char* pszPwd)
{
    uint32_t nBytes = 0;
    HZIP hZip = OpenZip(pszInFile, pszPwd);
    ZIPENTRY ze = { 0 };
    if (ZR_OK == GetZipItem(hZip, -1, &ze)) 
    {
        for (int i = 0;i < ze.index;++i)
        {
            GetZipItem(hZip, i, &ze);
            nBytes += ze.unc_size;
        }
    }
    return nBytes;
}

uint32_t ZipUtil::GetUnCompressSize(char* pData, int nData, const char* pszPwd)
{
    uint32_t nBytes = 0;
    HZIP hZip = OpenZip(pData, nData, pszPwd);
    ZIPENTRY ze = { 0 };
    if (ZR_OK == GetZipItem(hZip, -1, &ze))
    {
        for (int i = 0;i < ze.index;++i)
        {
            GetZipItem(hZip, i, &ze);
            nBytes += ze.unc_size;
        }
    }
    return nBytes;
}

uint32_t ZipUtil::GuessCompressSize(int nData/*压缩前大小*/)
{
    return (nData > 0x1000) ? nData : 0x1000;
}

//using voidfun = bool(*)(const char*, uint64_t, void*);
//template <typename F>
//voidfun lamb2func(F lambda) {
//    static auto lambdabak = lambda;
//    return [](const char*, uint64_t, void*) ->bool { return lambdabak(); };
//}

class lamb2func
{
public:
    template <typename L>
    lamb2func(L lambda)
    {

    }

    template <typename F>
    operator F()
    {
        return F();
    }
};

bool ZipUtil::Compress(const char* pszInFolder, const char* pszOutFile, const char* pszPwd)
{
    struct StruFile {
        uint32_t nData = 0;
        std::vector<std::string> vtFile;
        bool isSucc;
    }struFile;
    FileUtil::EnumFiles(pszInFolder, -1, lamb2func([](bool isFile, char *filePath, uint32_t fileLen, void *p)-> int {
        StruFile* pStruFile = (StruFile*)p;
        if (fileLen >= 0xEFFFFFFF || fileLen < 1) { pStruFile->isSucc = false; return false; }
        pStruFile->nData += fileLen;
        if (pStruFile->nData >= 0xEFFFFFFF) { pStruFile->isSucc = false; return false; }
        pStruFile->vtFile.emplace_back(filePath);
        return true;
    }), &struFile);

    if (!struFile.isSucc) { return false; }

    HZIP hZip = CreateZip(pszOutFile, pszPwd);
    int nLenInFolder = strlen(pszInFolder);
    for (auto& it : struFile.vtFile)
    {
        if (ZR_OK != ZipAdd(hZip, it.substr(it.find(pszInFolder) + nLenInFolder).c_str(), it.c_str()))
        {
            CloseZip(hZip);
            return false;
        }
    }

    CloseZip(hZip);
    return true;
}

bool ZipUtil::Compress(const char* pszInFolder, char* pDataout, int* pnDataout, const char* pszPwd)
{
    struct StruFile {
        uint32_t nData = 0;
        std::vector<std::string> vtFile;
        bool isSucc;
    }struFile;
    FileUtil::EnumFiles(pszInFolder, -1, xUtils::lambda2func<bool(*)(bool,const char*, uint64_t, void*)>([](bool isFile, char *filePath, uint32_t fileLen, void *p)-> int {
        StruFile* pStruFile = (StruFile*)p;
        if (fileLen >= 0xEFFFFFFF || fileLen < 1) { pStruFile->isSucc = false; return false; }
        pStruFile->nData += fileLen;
        if (pStruFile->nData >= 0xEFFFFFFF) { pStruFile->isSucc = false; return false; }
        pStruFile->vtFile.emplace_back(filePath);
        return true;
    }), &struFile);

    if (!struFile.isSucc) { return false; }

    HZIP hZip = CreateZip(nullptr, (struFile.nData>0x1000) ? struFile.nData : 0x1000, pszPwd);
    int nLenInFolder = strlen(pszInFolder);
    for (auto& it : struFile.vtFile)
    {
        if (ZR_OK != ZipAdd(hZip, it.substr(it.find(pszInFolder) + nLenInFolder).c_str(), it.c_str())) 
        {
            CloseZip(hZip);
            return false;
        }
    }

    return getZipMem(hZip, pDataout, pnDataout);
}

bool ZipUtil::Compress(char* pData, int nData, char* pDataout, int* pnDataout, const char* pszPwd, const char* pszName)
{
    HZIP hZip = CreateZip(nullptr, (nData>0x1000) ? nData : 0x1000, pszPwd);

    if (ZR_OK != ZipAdd(hZip, pszName, pData, nData)) { CloseZip(hZip); return false; }

    return getZipMem(hZip, pDataout, pnDataout);
}

bool ZipUtil::UnCompress(const char* pszInFile, const char* pszOutFolder, const char* pszPwd)
{
    HZIP hZip = OpenZip(pszInFile, pszPwd);
    return unCompress(hZip, pszOutFolder);
}

bool ZipUtil::UnCompress(const char* pszInFile, char* pDataout, int* pnDataout, const char* pszPwd)
{
    HZIP hZip = OpenZip(pszInFile, pszPwd);
    return unCompress(hZip, pDataout, pnDataout);
}


bool ZipUtil::UnCompress(char* pData, int nData, const char* pszOutFolder, const char* pszPwd)
{
    HZIP hZip = OpenZip((void*)pData, nData, pszPwd);
    return unCompress(hZip, pszOutFolder);
}

bool ZipUtil::UnCompress(char* pData, int nData, char* pDataout, int* pnDataout, const char* pszPwd)
{
    HZIP hZip = OpenZip((void*)pData, nData, pszPwd);
    return unCompress(hZip, pDataout, pnDataout);
}

uint32_t ZipUtil::GetItemCount()
{
    ZIPENTRY ze = { 0 };
    if (ZR_OK != GetZipItem(m_hZip, -1, &ze)) { return 0; }
    return ze.index;
}

ZipUtil::ItemZipped ZipUtil::GetItemInfo(int index)
{
    ItemZipped iz = { 0 };
    static ZIPENTRY ze = { 0 };
    if (ZR_OK == GetZipItem(m_hZip, index, &ze))
    {
        strcpy(iz.szName, ze.name);
        iz.index = index;
        iz.zSize = (uint32_t)ze.comp_size;
        iz.uzSize = (uint32_t)ze.unc_size;
    }
    return iz;
}

ZipUtil::ItemZipped ZipUtil::GetItemInfo(const char* pszItemFile)
{
    ItemZipped iz = { 0 };
    uint32_t count = GetItemCount();
    for (int i = 0; i < count; ++i)
    {
        ZIPENTRY ze = { 0 };
        if (ZR_OK == GetZipItem(m_hZip, i, &ze) && strcmp(pszItemFile, ze.name) == 0)
        {
            strcpy(iz.szName, ze.name);
            iz.index = i;
            iz.zSize = (uint32_t)ze.comp_size;
            iz.uzSize = (uint32_t)ze.unc_size;
            break;
        }
    }

    return iz;
}

bool ZipUtil::AppendItem(const char* pszInFile, const char* pszItemFile, bool isCover)
{
    return (ZR_OK == ZipAdd(m_hZip, pszItemFile, pszInFile));
}

bool ZipUtil::AppendItem(char* pData, int nData, const char* pszItemFile, bool isCover)
{
    return (ZR_OK == ZipAdd(m_hZip, pszItemFile, pData, nData));
}


bool ZipUtil::DeleteItem(const char* pszItemFile)
{
    ZIPENTRY ze = { 0 };
    if (ZR_OK != GetZipItem(m_hZip, -1, &ze)) { return false; }

    HZIP hZip = NULL;
    if (m_fileMode) { hZip = CreateZip(m_szZipFileTemp, m_szPwd); }
    else 
    {
        uint32_t nDataTemp = ze.unc_size > 0x1000 ? ze.comp_size : 0x1000;
        m_pZipDataTemp = (char*)malloc(nDataTemp);
        hZip = CreateZip(m_pZipDataTemp, nDataTemp, m_szPwd);
    }

    uint32_t num = ze.index;
    for (uint32_t i = 0; i < num; ++i)
    {
        GetZipItem(m_hZip, i, &ze);
        if (strcmp(pszItemFile, ze.name) != 0)
        {
            char* pItem = (char*)malloc(ze.unc_size);
            UnzipItem(m_hZip, i, pItem, ze.unc_size);

            ZipAdd(hZip, ze.name, pItem, ze.unc_size);
        }
    }

    CloseZip(m_hZip);
    m_hZip = hZip;
    if (m_fileMode)
    {
        FileUtil::DeleteDirFiles(m_szZipFile, false);
        FileUtil::CopyDirFiles(m_szZipFileTemp, m_szZipFile, true);
    }
    else 
    {
        free(m_pZipData); 
        m_pZipData = m_pZipDataTemp; 
        m_pZipDataTemp = nullptr;
    }
    
    return true;
}

bool ZipUtil::ExtractItem(const char* pszItemFile, const char* pszOutFile)
{
    ZIPENTRY ze = { 0 };
    int index = 0;
    if (ZR_OK != FindZipItem(m_hZip, pszItemFile, true, &index, &ze) || index < 0) { return false; }

    return (ZR_OK != UnzipItem(m_hZip, index, pszOutFile));
}

bool ZipUtil::ExtractItem(const char* pszItemFile, char* pDataout, int* pnDataout)
{
    ZIPENTRY ze = { 0 };
    int index = 0;
    if (ZR_OK != FindZipItem(m_hZip, pszItemFile, true, &index, &ze) || index < 0) { return false; }

    if (*pnDataout < ze.unc_size) { return false; }

    if (ZR_OK != UnzipItem(m_hZip, index, pDataout, ze.unc_size)) { return false; }

    *pnDataout = ze.unc_size;

    return true;
}

bool ZipUtil::ExtractItem(int index, const char* pszOutFile)
{
    return (ZR_OK != UnzipItem(m_hZip, index, pszOutFile));
}

bool ZipUtil::ExtractItem(int index, char* pDataout, int* pnDataout)
{
    ZIPENTRY ze = { 0 };
    if (ZR_OK == GetZipItem(m_hZip, index, &ze)) 
    {
        if (*pnDataout < ze.unc_size) { return false; }
        if (ZR_OK != UnzipItem(m_hZip, index, pDataout, ze.unc_size)) { return false; }
        *pnDataout = ze.unc_size;
        return true; 
    }
    return false;
}

bool ZipUtil::unCompress(HZIP hZip, const char* pszOutFolder)
{
    bool isSucc = false;
    do
    {
        ZIPENTRY ze = { 0 };
        if (ZR_OK != GetZipItem(hZip, -1, &ze)) { goto __label_exit__; }
        int num = ze.index;
        if (ZR_OK != SetUnzipBaseDir(hZip, pszOutFolder)) { goto __label_exit__; }
        for (int i = 0; i < num; ++i)
        {
            GetZipItem(hZip, i, &ze);
            if (ZR_OK != UnzipItem(hZip, i, ze.name)) { goto __label_exit__; }
        }
        isSucc = true;
    } while (false);

__label_exit__:
    CloseZip(hZip);
    return isSucc;
}



bool ZipUtil::OutputAsFile(const char* pszZipFile)
{
    char* pZip = nullptr;
    unsigned long nZip = 0;
    ZipGetMemory(m_hZip, (void**)&pZip, (unsigned long*)&nZip); // uz.h表明ZipGetMemory之后m_hZip将失效，必须重新初始化
    FileUtil::ReadWrite(pszZipFile, pZip, nZip, false);
    return reOpenZip(pZip, nZip);
}

bool ZipUtil::OutoutAsData(char* pDataout, int* pnDataout)
{
    char* pZip = nullptr;
    unsigned long nZip = 0;
    ZipGetMemory(m_hZip, (void**)&pZip, (unsigned long*)&nZip); // uz.h表明ZipGetMemory之后m_hZip将失效，必须重新初始化

    do
    {
        if (*pnDataout < nZip) { break; }
        memcpy(pDataout, pZip, nZip);
        *pnDataout = nZip;

    } while (false);

    return reOpenZip(pZip, nZip);
}

bool ZipUtil::GetCompressSize()
{
    return true;
}

bool ZipUtil::unCompress(HZIP hZip, char* pDataout, int* pnDataout)
{
    bool isSucc = false;
    do
    {
        ZIPENTRY ze = { 0 };
        if (ZR_OK != GetZipItem(hZip, -1, &ze)) { goto __label_exit__; }
        int num = ze.index;
        uint32_t nDataTemp = 0;
        for (int i = 0; i < num; ++i)
        {
            GetZipItem(hZip, i, &ze);
            if (nDataTemp + ze.unc_size > *pnDataout) { goto __label_exit__; }
            UnzipItem(hZip, i, &pDataout[nDataTemp], ze.unc_size);
            nDataTemp += ze.unc_size;
        }
        isSucc = true;
    } while (false);

__label_exit__:
    CloseZip(hZip);
    return isSucc;
}

bool ZipUtil::getZipMem(HZIP hZip, char* pDataout, int* pnDataout)
{
    bool isSucc = false;

    char *pDataTemp = nullptr;
    unsigned long nDataTemp = 0;
    if (ZR_OK == ZipGetMemory(hZip, (void**)&pDataTemp, (unsigned long*)&nDataTemp))
    {
        if (nDataTemp <= (*pnDataout))
        {
            memcpy(pDataout, pDataTemp, nDataTemp);
            isSucc = true;
        }
        *pnDataout = nDataTemp;
    }

    CloseZip(hZip);
    return isSucc;
}

bool ZipUtil::reOpenZip(char* pZip, uint32_t nZip)
{
    HZIP hZip = NULL;
    if (m_fileMode)
    {
        hZip = OpenZip(m_szZipFile, m_szPwd);
    }
    else
    {
        free(m_pZipData);
        m_pZipData = (char*)calloc(1, nZip);
        memcpy(m_pZipData, pZip, nZip);
        hZip = OpenZip(m_pZipData, nZip, m_szPwd);
    }
    CloseZip(m_hZip);
    m_hZip = hZip;
    return true;
}
